package com.youzi.tunnel.server.work;


import com.alibaba.fastjson.JSONObject;
import com.youzi.tunnel.common.protocol.Constants;
import com.youzi.tunnel.common.protocol.Message;
import com.youzi.tunnel.server.entity.Tunnel;
import com.youzi.tunnel.server.handlers.TunnelServerChannelHandler;
import com.youzi.tunnel.server.manager.ChannelManager;
import com.youzi.tunnel.server.manager.ClientManager;
import com.youzi.tunnel.server.manager.TunnelManager;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import lombok.extern.slf4j.Slf4j;

import java.net.InetSocketAddress;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.ExecutionException;


@Slf4j
public class UserServerBootstrap {
    private static ServerBootstrap serverBootstrap;

    /*
     * 开启端口
     * */
    public static ChannelFuture start(Integer port) throws InterruptedException, ExecutionException {
        initServerBootstrap();
        return serverBootstrap.bind(port).sync();
    }


    private static void initServerBootstrap() {
        if (serverBootstrap == null) {
            serverBootstrap = new ServerBootstrap();
            NioEventLoopGroup serverBossGroup = new NioEventLoopGroup(8);
            NioEventLoopGroup serverWorkerGroup = new NioEventLoopGroup(8);

            serverBootstrap.group(serverBossGroup, serverWorkerGroup)
                    .channel(NioServerSocketChannel.class)
                    .childHandler(new ChannelInitializer<SocketChannel>() {

                        @Override
                        protected void initChannel(SocketChannel channel) {
                            ChannelManager.updateChannel(channel);
                            try {


                                InetSocketAddress address = (InetSocketAddress) (channel.localAddress());
                                //通过端口获取到链路信息
                                Tunnel tunnel = TunnelManager.getTunnel(address.getPort());

                                assert tunnel != null;
                                log.info("服务端端口[{}]收到连接消息, 发送开启链路请求信息,[{}]", tunnel.getToPort(), tunnel.toString());
                                //设置不可读, 远程链路打开后才可读
                                channel.config().setOption(ChannelOption.AUTO_READ, false);
                                //标记此链路是属于直接在服务端监听端口的, 服务器在转发数据时需要这个标记判断数据转发给客户端还是直接转发到端口链路
                                channel.attr(Constants.CHANNEL_ON_SERVER).set(true);

                                //发送开启链路请求信息
                                Message message = new Message();
                                message.setContent(Constants.STATE.REQUEST.value);
                                message.setType(Message.TYPE.OPEN_CONNECT);

                                //携带上channelId,以便fromClient开启链路后绑定链路
                                tunnel.setChannelId(channel.id().toString());
                                message.setData(JSONObject.toJSONString(tunnel).getBytes(StandardCharsets.UTF_8));

                                Channel fromChannel = ClientManager.getClient(tunnel.getFromClient()).getChannel();
                                //使用客户端链路发送这条消息, 以便服务端绑定链路
                                fromChannel.writeAndFlush(message);

                                channel.pipeline().addLast(new TunnelServerChannelHandler());
                            } catch (Exception e) {
                                log.info("请求开启链路异常!");
                            }
                        }

                        @Override
                        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
                            cause.printStackTrace();
                            super.exceptionCaught(ctx, cause);
                        }
                    });
        }

    }
}
